package net.esj.basic.dao.jdbc.jpa.support;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;

import net.esj.basic.dao.jdbc.jpa.Column;
import net.esj.basic.dao.jdbc.jpa.Query;
import net.esj.basic.dao.jdbc.jpa.Session;
import net.esj.basic.dao.jdbc.jpa.SessionFactory;
import net.esj.basic.dao.jdbc.jpa.TableBeanMapping;

import org.apache.commons.lang.NotImplementedException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Transaction;

public abstract class SessionSupport implements Session {
	  private static final Log logger = 
		  LogFactory.getLog(SessionSupport.class);
	SessionFactory sessionFactory;
	Connection connection;

	public void close() {
		try {
			connection.close();
		} catch (SQLException e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	public abstract Query createQuery(String sql);
	public Connection getConnection() {
		return connection;
	}

	public void setConnection(Connection connection) {
		this.connection = connection;
	}



	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	private Object getProValue(Object entity, String proName){
        BeanInfo beanInfo = null;
        try {
			beanInfo = Introspector.getBeanInfo(entity.getClass());
		} catch (IntrospectionException e) {
			throw new RuntimeException(e.getMessage(),e);
		}
    	PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
    	for (PropertyDescriptor pd : propertyDescriptors) {
    		String proNamex = pd.getName();
    		if(proNamex.equalsIgnoreCase(proName)){
    			try {
					return pd.getReadMethod().invoke(entity, null);
				} catch (Exception e) {
					throw new RuntimeException(e.getMessage(),e);
				}
    		}
    	}
    	throw new RuntimeException(entity.getClass()+"没有该属性：["+proName+"]。");
	}
	/**
	 * TODO 新插入时数据库支持对主键的返回，但具体做法我还不清楚。
	 * 
	 * @param mapping
	 * @param entity
	 * @return 插入的对象的主键的值。
	 */
	public Object insert(Object entity) {
		Class entityType = entity.getClass();
		TableBeanMapping mapping = sessionFactory.getTableBeanMapping(entityType);
		String tableName = mapping.getTableName();
		String sql = "insert into " + tableNameWrap(tableName)+ " ";
		Query q = this.createQuery(null);
		String colNameStr = "";
		String qMarkStr = "";
		Column[] cols = mapping.getColumns();
		PropertyDescriptor[] propertyDescriptors = mapping.getPropertyDescriptors();
		int i = 0;
		for (Column column : cols) {
			String columnName = column.getColumnName();
			PropertyDescriptor pd = propertyDescriptors[i];
			String proName = pd.getName();
			if (i != 0) {
				colNameStr += ',';
				qMarkStr += ',';
			}
			colNameStr += columnNameWrap(columnName);
			qMarkStr += '?';
			q.setParameter(i+1, getProValue(entity,proName));
			i++;
		}
		sql += "(" + colNameStr + ") values (" + qMarkStr + ")";
		if(logger.isDebugEnabled())logger.debug("sql=["+sql+"]");
		q.setSql(sql);
		q.executeUpdate();
		return entity;// TODO 看方法备注
	}

	/**
	 * 更新。
	 * 
	 * @param mapping
	 * @param entity
	 * @return
	 */
	public Object update(Class entityType, Map entity) {
		TableBeanMapping mapping = sessionFactory.getTableBeanMapping(entityType);
		String tableName = mapping.getTableName();
		String sql = "update " + tableNameWrap(tableName) + " ";
		Query q = this.createQuery(null);
		int cnt = 1;
		Column[] cols = mapping.getColumns();
		for (Column column : cols) {
			String columnName = column.getColumnName();
			if (entity.containsKey(columnName)) {
				if (cnt == 1) {
					sql += " set ";
				}else{
					sql += ',';
				}
				sql += " " + columnNameWrap(columnName) + "=?";
				q.setParameter(cnt, entity.get(columnName));
				cnt++;
			}
		}
		String pkName = getPkColumn(entityType).getColumnName();//不支持复合主键
		sql += " where "+columnNameWrap(pkName)+"=?";
		q.setParameter(cnt, entity.get(pkName));
		
		if(logger.isDebugEnabled())
		logger.debug("sql=["+sql+"]");
		q.setSql(sql);
		q.executeUpdate();
		return entity;
	}

	public int delete(Class entityType, Serializable pk) {
		TableBeanMapping mapping = sessionFactory.getTableBeanMapping(entityType);
		String tableName = mapping.getTableName();
		String pkName = getPkColumn(entityType).getColumnName();//不支持复合主键
		return deleteByUnique(entityType, pkName, pk);
	}
	public int delete(Class entityType, Object entity) {
		TableBeanMapping mapping = sessionFactory.getTableBeanMapping(entityType);
		Map m = (Map)entity;
		String pkName = getPkColumn(entityType).getColumnName();//不支持复合主键
		return deleteByUnique(entityType, pkName, m.get(pkName));
	}

	public Object findByUnique(Class entityType, String colName, Object v) {
		TableBeanMapping mapping = sessionFactory.getTableBeanMapping(entityType);
		String tableName = mapping.getTableName();
		String sql = "select * from " + tableNameWrap(tableName) + " where " + columnNameWrap(colName)
				+ "=?";
		Query q = this.createQuery(sql);
		q.setParameter(1, v);
		return q.getSingleResultAs(entityType);
	}

	public Object get(Class entityType, Serializable pk) {
		TableBeanMapping mapping = sessionFactory.getTableBeanMapping(entityType);
		String pkName = getPkColumn(entityType).getColumnName();//不支持复合主键
		return findByUnique(entityType, pkName, pk);
	}

	public int deleteByUnique(Class entityType, String colName, Object v) {
		TableBeanMapping mapping = sessionFactory.getTableBeanMapping(entityType);
		String tableName = mapping.getTableName();
		String sql = "delete from " + tableNameWrap(tableName) + " where " + columnNameWrap(colName) + "=?";
		Query q = this.createQuery(sql);
		q.setParameter(1, v);
		return q.executeUpdate();
	}
	public Transaction beginTransaction() {
		throw new NotImplementedException("beginTransaction");
	}
	public Transaction getTransaction() {
		throw new NotImplementedException("getTransaction");
	}
	
	private Column getPkColumn(Class entityType){
		TableBeanMapping mapping = sessionFactory.getTableBeanMapping(entityType);
		Column[] pks = mapping.getPkColumns();
		if(pks.length != 1){
//			Exception e =  new NotSupportedException("尚未支持复合主键的情况 tableName=["+database.getTableSchema()+"."+mapping.getTableName()+"]");
//			throw new RuntimeException(e.getMessage(),e);
			if(logger.isWarnEnabled()){
				logger.warn("尚未支持复合主键的情况 tableName=["+mapping.getTableName()+"]，本表主键数量为"+pks.length);
			}
		}
		return pks[0];
	}
	protected String columnNameWrap(String colName){
		return colName;
	}
	protected String tableNameWrap(String tableName){
		return tableName;
	}

	public Object merge(Object entity) {
		Class entityType = entity.getClass();
		TableBeanMapping mapping = sessionFactory.getTableBeanMapping(entityType);
		Column pkCol = getPkColumn(entityType);
		String pkName = null;
		Object pk = null;
		if(pkCol != null)pkName = pkCol.getColumnName();//不支持复合主键
		if(pkName != null)pk = getProValue(entity,pkName);
		if (pk == null || "".equals(pk)) {
			return insert( entity);
		} else {
			return update( entity);
		}
	}

	public Object update(Object entity) {
		// TODO Auto-generated method stub
		return null;
	}
	
	private final Object newinstance(Class entityType){
		Object bean;
		try {
			bean = entityType.newInstance();
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(),e);
		}
		return bean;
	}
}
